﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Beeflys
{
    public static class World
    {
        private static readonly ReadOnlyMemory<char> SPLIT_LINE = "/".AsMemory();
        
        static World()
        {
            system = new ActorFactory(null);
            root = system.AddActorAsync(typeof(Root), ActorId.DEFAULT_HOST, Root.ROOT).GetAwaiter().GetResult().Entity();
        }

        private static IEntityRef root;
        private static ActorFactory system;
        
        public static async Task<IActorRef> AddActorAsync<TActor>(string name)
            where TActor : AActor, new()
        {
            var actorRef = await root.AddChildAsync<TActor>(name);
            return actorRef;
        }
        public static Task<IActorRef> AddActorAsync<TActor, T1>(string name,
            T1 item1)
            where TActor : AActor, new()
        {
            return root.AddChildAsync<TActor, T1>(name, item1);
        }
        public static Task<IActorRef> AddActorAsync<TActor, T1, T2>(string name,
            T1 item1, T2 item2)
            where TActor : AActor, new()
        {
            return root.AddChildAsync<TActor, T1, T2>(name, item1, item2);
        }
        public static Task<IActorRef> AddActorAsync<TActor, T1, T2, T3>(string name,
            T1 item1, T2 item2, T3 item3)
            where TActor : AActor, new()
        {
            return root.AddChildAsync<TActor, T1, T2, T3>(name, item1, item2, item3);
        }
        public static async Task<IActorRef> AddActorAsync<TActor, T1, T2, T3, T4>(string name,
            T1 item1, T2 item2, T3 item3, T4 item4)
            where TActor : AActor, new()
        {
            return await root.AddChildAsync<TActor, T1, T2, T3, T4>(name, item1, item2, item3, item4);
        }
        public static Task<IActorRef> AddActorAsync<TActor, T1, T2, T3, T4, T5>(string name,
            T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
            where TActor : AActor, new()
        {
            return root.AddChildAsync<TActor, T1, T2, T3, T4, T5>(name, item1, item2, item3, item4, item5);
        }
        public static Task<IActorRef> AddActorAsync<TActor, T1, T2, T3, T4, T5, T6>(string name,
            T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
            where TActor : AActor, new()
        {
            return root.AddChildAsync<TActor, T1, T2, T3, T4, T5, T6>(name, item1, item2, item3, item4, item5, item6);
        }

        public static Task<IActorRef> AddActorAsync<TActor, T1, T2, T3, T4, T5, T6, T7>(string name,
            T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
            where TActor : AActor, new()
        {
            return root.AddChildAsync<TActor, T1, T2, T3, T4, T5, T6, T7>(name, item1, item2, item3, item4, item5, item6, item7);
        }

        internal static async Task<IActorRef> AddActorAsync<TActor>(string host, string name)
            where TActor : AActor, new()
        {
            var actorRef = await root.AddChildAsync<TActor>(host, name);
            return actorRef.Fabled();
        }
        
        public static Task<IActorRef> GetActorAsync(string name)
        {
            return root.GetChildAsync(name);
        }

        public static Task<bool> DestroyActorAsync(string name)
        {
            return root.DestroyChildAsync(name);
        }

        public static Task<IActorRef> AddActorFromTreeAsync<TActor>(string fullname)
            where TActor : AActor
        {
            return AddActorFromTreeAsync<TActor>(ActorId.DEFAULT_HOST, fullname);
        }

        public static async Task<IActorRef> AddActorFromTreeAsync<TActor>(string host, string fullName)
            where TActor : AActor
        {
            List<string> nodeArr = ParseFullName(fullName);
            string addName = nodeArr[nodeArr.Count - 1];
            nodeArr = nodeArr.GetRange(0, nodeArr.Count - 1);
            IActorRef lastNode = await InternalGetActorFromTreeAsync(nodeArr);

            if(lastNode == null)
            {
                return null;
            }

            return await lastNode.Entity().AddChildAsync<TActor>(host, addName);
        }

        public static Task<IActorRef> GetActorFromTreeAsync(string fullname)
        {
            var arr = ParseFullName(fullname);
            return InternalGetActorFromTreeAsync(arr);
        }

        public static async Task<bool> DestroyActorFromTreeAsync(string fullName)
        {
            List<string> nodeArr = ParseFullName(fullName);
            string delName = nodeArr[nodeArr.Count - 1];
            nodeArr = nodeArr.GetRange(0, nodeArr.Count - 1);
            IActorRef lastNode = await InternalGetActorFromTreeAsync(nodeArr);

            if(lastNode == null)
            {
                return false;
            }

            return await lastNode.Entity().DestroyChildAsync(delName);
        }

        private static async Task<IActorRef> InternalGetActorFromTreeAsync(List<string> nodes)
        {
            IActorRef node = root;
            for (int i = 0; i < nodes.Count; i++)
            {
                var child = await node.Entity().GetChildAsync(nodes[i]);

                if (child == null)  return null;

                node = child;
            }

            return node;
        }

        private static List<string> ParseFullName(string fullName)
        {
            ReadOnlySpan<char> strSpan = fullName.AsSpan();
            if (strSpan.IndexOf(Root.ROOT_MEMORY.Span) != 0)
            {
                throw new Exception("The fullName format is incorrect. The correct format is root");
            }
            
            List<string> arr = new List<string>();
            strSpan = strSpan.Slice(Root.ROOT_MEMORY.Length);
            while (true)
            {
                var index = strSpan.IndexOf(SPLIT_LINE.Span);
                if (index == 0)
                {
                    throw new Exception("fullName 异常");
                }
                if (index < 0)
                {
                    arr.Add(strSpan.ToString());
                    break;
                }
                else
                {
                    arr.Add(strSpan.Slice(0, index).ToString());
                    strSpan = strSpan.Slice(index + SPLIT_LINE.Length);
                }
            }

            return arr;
        }
    }
}